/***************************************************************************
 *   Copyright (C) 2007 by crackedmind                                     *
 *   digital.stream.of.mind@gmail.com                                      *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "precompiled.hpp"
#include "jamfs_log.hpp"
#include "pugixml.hpp"
#include "jamfs_filesystem.hpp"

#include "jamfs_ifolder.hpp"

typedef jamfs::IFolder* (*GETFOLDER)(); 

namespace jamfs
{
    struct DeleteObject
    {
        template <typename T>
        void operator() (const T* ptr) const
        {
            g_Log.log(LOG_DEBUG, "Free %p", ptr);
            delete ptr;
        }
    };

    Filesystem::Filesystem(uint8_t log_level, const char *config_file /* "jamfs.xml */ )
        :__log_level(0), __config_file(config_file)
    {
        g_Log.open("jamfs.log", log_level );

#ifdef PLATFORM_WIN32
        __last_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
#endif
    }

    Filesystem::~Filesystem()
    {
#ifdef PLATFORM_WIN32
        SetErrorMode(__last_error_mode);
#endif
        if ( !__loaded_plugins.empty() ) {
            std::for_each(__loaded_plugins.begin(), __loaded_plugins.end(), DeleteObject());
            __loaded_plugins.erase(__loaded_plugins.begin(), __loaded_plugins.end());
        }
    }

    bool Filesystem::plugin_exist(const char *filename)
    {
#if defined PLATFORM_WIN32
        struct _stat buf;
        int result = _stat(filename, &buf);
#elif defined PLATFORM_LINUX
        struct stat buf;
        int result = stat(filename, &buf);
#endif

        if ( result != 0 )
            return false;

        return true;
    }

    bool Filesystem::process_config()
    {
        using namespace pugi;
        const char *path_to_plugin;
        pugi::xml_document __config;

        if ( __config.load_file(__config_file) == false ) {
            g_Log.log(LOG_ERROR, "Can't find %s", __config_file);
            return false;
        }

        xml_node codecs = __config.first_element_by_path("/jamfs/codecs");

        if ( codecs.empty() )
            g_Log.log(LOG_DEBUG, "Can't get element by path /jamfs/codecs");

        for ( xml_node codec = codecs.child("shared_object"); codec; codec = codec.next_sibling("shared_object")) {
            path_to_plugin = codec.attribute("file").value();

            if ( this->plugin_exist(path_to_plugin) ) {
                g_Log.log(LOG_INFO, "Try to load %s", path_to_plugin);
                    
                SharedLibrary *lib = new SharedLibrary(path_to_plugin);
                
                if ( lib->load() ) {
                    __loaded_plugins.push_back(lib);
                    g_Log.log(LOG_INFO, "Loading of %s is successfull", path_to_plugin);

                    GETFOLDER getFolder = (GETFOLDER)lib->symbol("getFolder");
                    
                    if ( getFolder != NULL ) {
                        g_Log.log(LOG_DEBUG, "Successful getting of function from %s", path_to_plugin);
                        
                        IFolder *folder = getFolder();
                        
                        if ( this->register_folder(folder) == true ) {
                            g_Log.log(LOG_DEBUG, "Successful registering of folder - %s", folder->type().c_str());
                        } else
                            g_Log.log(LOG_ERROR, "Can't register folder. Maybe already registred");
                    } else
                        g_Log.log(LOG_ERROR, "Can't get function from %s", path_to_plugin);

                } else {
                    g_Log.log(LOG_ERROR, "Failed to load %s. It isn't valid shared library.", path_to_plugin);
                    delete lib;
                }
            }
            else
                g_Log.log(LOG_ERROR, "Plugin %s doesn't exist.", path_to_plugin);

        }

        xml_node mounts = __config.first_element_by_path("/jamfs/mounts");

        if ( mounts.empty() )
            g_Log.log(LOG_DEBUG, "Can't get element by path /jamfs/mounts");

        for (xml_node m_point = mounts.child("mount"); m_point; m_point = m_point.next_sibling("mount")) {
            const char *folder = m_point.attribute("folder").value();
            const char *name = m_point.attribute("name").value();
            const char *type = m_point.attribute("type").value();

            this->mount(name, folder, type);
        }

        return true;
    }

    bool Filesystem::register_folder ( IFolder *folder )
    {
        std::pair<__reged_folders_map::iterator,bool> pa = __reged_folders.insert(
            __reged_folders_map::value_type(folder->type(), folder));

        return pa.second;
    }

    bool Filesystem::unregister_folder ( IFolder *folder )
    {
        __reged_folders_map::size_type res = __reged_folders.erase(folder->type());

        return ( res != 0 ? true : false);
        return false;
    }

    bool Filesystem::mount( const char *dir, const char *folder, const char *type )
    {
        _mount_point *new_point = new _mount_point;

        new_point->dir = dir;
        new_point->folder = folder;
        new_point->type = type;

        __mounted_points.push_back(new_point);

        g_Log.log(LOG_INFO, "Mounted: %s to %s of type %s", folder, dir, type );

        return true;
    }

    bool Filesystem::umount( const char *node )
    {
        std::vector<_mount_point*>::iterator it;

        for ( it = __mounted_points.begin(); it != __mounted_points.end(); ) {
            if ( ( ( *it )->dir.find ( node ) != std::string::npos ) || ( ( *it )->folder.find ( node ) != std::string::npos ) ) {
                delete ( *it );
                it = __mounted_points.erase ( it );
                
                g_Log.log(LOG_INFO, "Umount: %s", node );

                return true;
            } else
                ++it;
        }
        return false;
    }

    void Filesystem::close( IFile *file )
    {

    }

    IFile *open ( const char *filename )
    {
        return NULL;
    }
}
